﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Sofire.Dynamic.Factories;
using Sofire.Dynamic;
using System.Reflection;

namespace Sofire.Data
{
    /// <summary>
    /// 实体与数据行的映射。
    /// </summary>
    public class EntityMapper : IEntityMapper
    {
        /// <summary>
        ///表示 <see cref="Sofire.Data.IEntityMapper"/>的类型。
        /// </summary>
        public readonly static Type IType = typeof(IEntityMapper);

        private Dictionary<string, HandlerInfo<DynamicSetMemberHandler>> inner;

        private EntityMapper(int capacity)
        {
            inner = new Dictionary<string, HandlerInfo<DynamicSetMemberHandler>>(capacity, StringComparer.CurrentCultureIgnoreCase);
        }

        private Type _InstanceType;
        /// <summary>
        /// 设置或获取实例的类型。
        /// </summary>
        public Type InstanceType
        {
            get
            {
                return this._InstanceType;
            }
        }

        /// <summary>
        /// 将指定的数据行填充到实体。
        /// </summary>
        /// <param name="form">数据行。</param>
        /// <param name="to">实例。</param>
        public virtual void FillEntity(DataRow form, object to)
        {
            if(to == null) return;
            HandlerInfo<DynamicSetMemberHandler> handlerInfo;
            string name;
            object value;
            PropertyInfo property;
            Type propertyType;

            foreach(DataColumn column in form.Table.Columns)
            {
                name = column.ColumnName;
                value = form[column];

                if(!inner.TryGetValue(name, out handlerInfo))
                {
                    if(QueryEngineBase.IgnoreUnfoundEntityProperty) continue;
                    throw new KeyNotFoundException("填充数据时候发生错误，无法在 " + to.GetType().FullName + " 找到字段 " + name + " 对应的属性成员。");
                }
                property = handlerInfo.Member as PropertyInfo;
                propertyType = property.PropertyType;

                if(Convert.IsDBNull(value))
                    value = propertyType.GetDefaultValue();
                else if(column.DataType != propertyType)
                    value = GA.ChangeType(value, propertyType);
                handlerInfo.Handler(to, this.CheckValue(to, property, value));
            }
        }

        /// <summary>
        /// 检查指定实例的属性值。
        /// </summary>
        /// <param name="instance">实例。</param>
        /// <param name="property">属性。</param>
        /// <param name="value">属性的值。</param>
        /// <returns>返回新的属性值。</returns>
        protected virtual object CheckValue(object instance, PropertyInfo property, object value) { return value; }

        private static readonly Dictionary<Type, IEntityMapper> Cacher
            = new Dictionary<Type, IEntityMapper>();

        /// <summary>
        /// 指定实例的数据类型，创建或从缓存读取一个设置器映射。
        /// </summary>
        /// <param name="type">实例的数据类型。</param>
        /// <returns>返回一个设置器的映射。</returns>
        public static IEntityMapper Create(Type type)
        {
            var attr = type.GetAttribute<EntityRowAttribute>();
            if(attr != null && attr.Entity != null) return attr.Entity;

            IEntityMapper collection;

            lock(type)
            {
                if(Cacher.TryGetValue(type, out collection)) return collection;

                if(EntityMapper.IType.IsAssignableFrom(type))
                {
                    collection = Activator.CreateInstance(type) as IEntityMapper;
                }
                else
                {
                    var ps = type.GetProperties();
                    var collection2 = new EntityMapper(ps.Length);
                    collection2._InstanceType = type;
                    foreach(var p in ps)
                    {
                        if(p.CanWrite)
                            collection2.inner.Add(p.Name
                                , new HandlerInfo<DynamicSetMemberHandler>(p
                                    , DynamicSetPropertyFactory.Instance.Create(p, type)));
                    }
                    collection = collection2;
                }
                Cacher.Add(type, collection);
                return collection;
            }
        }

    }
}
